package com.ls.fw.mongo.dao.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.bson.Document;
import org.bson.conversions.Bson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ls.fw.mongo.bean.IndexBean;
import com.ls.fw.mongo.dao.MongoDbClient;
import com.ls.fw.mongo.helper.MongoDbHelper;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientOptions.Builder;
import com.mongodb.MongoClientURI;
import com.mongodb.WriteConcern;
import com.mongodb.client.FindIterable;
import com.mongodb.client.ListIndexesIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;

public class MongoDbClientImpl implements MongoDbClient {

	private static final String MONGO_CONNECTIONSTR = "mongo.connectionstr";

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	private MongoClient mongoClient;
	
	private String connectionStr;

    public MongoDbClientImpl(String connectionStr) {
    	logger.info(connectionStr);
    	  // 从配置文件中获取属性值
        this.connectionStr = connectionStr;
        Builder options = new MongoClientOptions.Builder();
        // options.autoConnectRetry(true);// 自动重连true
        // options.maxAutoConnectRetryTime(10); // the maximum auto connect retry time
        options.connectionsPerHost(300);// 连接池设置为300个连接,默认为100
        options.connectTimeout(15000);// 连接超时，推荐>3000毫秒
        options.maxWaitTime(5000); //
        options.socketTimeout(0);// 套接字超时时间，0无限制
        options.threadsAllowedToBlockForConnectionMultiplier(5000);// 线程队列数，如果连接线程排满了队列就会抛出“Out of semaphores to get db”错误。
        options.writeConcern(WriteConcern.NORMAL);//
        options.build();
        MongoClientURI uri = new MongoClientURI(connectionStr,options);
      	mongoClient = new MongoClient(uri);
    }
    
    // ------------------------------------共用方法---------------------------------------------------

    
    /**
     * 获取DB实例 - 指定DB
     * 
     * @param dbName
     * @return
     */
    @Override
    public MongoDatabase getDB(String dbName) {
        if (dbName != null && !"".equals(dbName)) {
            MongoDatabase database = mongoClient.getDatabase(dbName);
            return database;
        }
        return null;
    }

    /**
     * 获取collection对象 - 指定Collection
     * 
     * @param collName
     * @return
     */
    @Override
    public MongoCollection<Document> getCollection(String dbName, String collName) {
        if (null == collName || "".equals(collName)) {
            return null;
        }
        if (null == dbName || "".equals(dbName)) {
            return null;
        }
        MongoCollection<Document> collection = null;
		collection = mongoClient.getDatabase(dbName).getCollection(collName);
        return collection;
    }

    /**
     * 查询DB下的所有表名
     */
    @Override
    public List<String> getAllCollections(String dbName) {
        MongoIterable<String> colls = getDB(dbName).listCollectionNames();
        List<String> _list = new ArrayList<String>();
        for (String s : colls) {
            _list.add(s);
        }
        return _list;
    }

    /**
     * 获取所有数据库名称列表
     * 
     * @return
     */
    @Override
    public MongoIterable<String> getAllDBNames() {
        MongoIterable<String> s = mongoClient.listDatabaseNames();
        return s;
    }

    /**
     * 删除一个数据库
     */
    @Override
    public void dropDB(String dbName) {
        getDB(dbName).drop();
    }

    /**
     * 查找对象 - 根据主键_id
     * 
     * @param collection
     * @param id
     * @return
     */
    @Override
    public Document findById(MongoCollection<Document> coll, String id) {
        Document myDoc = coll.find(Filters.eq(_ID, id)).first();
        return myDoc;
    }

    @Override
    public FindIterable<Document> findByIds(MongoCollection<Document> coll, String... id) {
    	FindIterable<Document> myDoc = null;
    	if(id==null|| id.length<=0){
    		 myDoc = coll.find();
    	}else{
    		 myDoc = coll.find(Filters.in(_ID, id));
    	}
        return myDoc;
    }

    /** 统计数 */
    @Override
    public int getCount(MongoCollection<Document> coll) {
        int count = (int) coll.count();
        return count;
    }

    /** 条件查询 */
    @Override
    public MongoCursor<Document> find(MongoCollection<Document> coll, Bson filter) {
        return coll.find(filter).iterator();
    }

    /** 分页查询 */
    @Override
    public MongoCursor<Document> findByPage(MongoCollection<Document> coll, Bson filter, int pageNo, int pageSize) {
        Bson orderBy = new BasicDBObject(_ID, 1);
        if(pageSize>0){
        	return coll.find(filter).sort(orderBy).skip((pageNo - 1) * pageSize).limit(pageSize).iterator();
        }
     	return coll.find(filter).sort(orderBy).iterator();
    }

    /**
     * 通过ID删除
     * 
     * @param coll
     * @param id
     * @return
     */
    @Override
    public long deleteById(MongoCollection<Document> coll, String... id) {
        long count = 0L;
        Bson filter = Filters.in(_ID, id);
        DeleteResult deleteResult = coll.deleteMany(filter);
        count = 1;//deleteResult.getDeletedCount();
        return count;
    }

    /**
     * FIXME
     * 
     * @param coll
     * @param id
     * @param newdoc
     * @return
     */
    @Override
    public long updateById(MongoCollection<Document> coll, String id, Document newdoc) {
        Bson filter = Filters.eq(_ID, id);
        UpdateResult updateResult = coll.updateOne(filter, new Document("$set", newdoc));
        return 1;//updateResult.getMatchedCount();
    }

    @Override
    public long save(MongoCollection<Document> coll, String id, Document newdoc) {
        Bson filter = Filters.eq(_ID, id);
        UpdateOptions options = new UpdateOptions();
	    //如果这里是true，当查不到结果的时候会添加一条newDoc,默认为false
	    options.upsert(true);
	    BasicDBObject updateObj = new BasicDBObject();
	    updateObj.put("$set", newdoc);
        UpdateResult updateResult = coll.updateOne(filter, updateObj,options);
        return 1;//updateResult.getMatchedCount();
    }
    @Override
    public long save(MongoCollection<Document> coll, String id, Map<String, Object> map) throws Exception {
        Bson filter = Filters.eq(_ID, id);
        UpdateOptions options = new UpdateOptions();
	    //如果这里是true，当查不到结果的时候会添加一条newDoc,默认为false
	    options.upsert(true);
	    BasicDBObject updateObj = new BasicDBObject();
	    updateObj.put("$set", MongoDbHelper.mapToDocument(map));
        UpdateResult updateResult = coll.updateOne(filter, updateObj,options);
        return 1;//updateResult.getMatchedCount();
    }
    @Override
    public long inc(MongoCollection<Document> coll, String id, Document newdoc) {
        Bson filter = Filters.eq(_ID, id);
        UpdateOptions options = new UpdateOptions();
	    //如果这里是true，当查不到结果的时候会添加一条newDoc,默认为false
	    options.upsert(true);
	    BasicDBObject updateObj = new BasicDBObject();
	    updateObj.put("$inc", newdoc);
        UpdateResult updateResult = coll.updateOne(filter, updateObj,options);
        return 1;//updateResult.getMatchedCount();
    }

    @Override
    public boolean exist(MongoCollection<Document> coll, String id){
    	return coll.count(Filters.eq(_ID, id))>0;
    }
    
    @Override
    public void dropCollection(String dbName, String collName) {
        getDB(dbName).getCollection(collName).drop();
    }

    /**
     * 关闭Mongodb
     */
    @Override
    public void close() {
        if (mongoClient != null) {
            mongoClient.close();
            mongoClient = null;
        }
    }

	public String getConnectionStr() {
		return connectionStr;
	}

	public void setConnectionStr(String connectionStr) {
		this.connectionStr = connectionStr;
	}

	@Override
	public void insert(String dbName,String table,Map<String, Object> map) throws Exception {
		 this.getCollection(dbName, table).insertOne(MongoDbHelper.mapToDocument(map));
	}

	@Override
	public void insert(String dbName,String table,List<Map<String, Object>> map) throws Exception {
		 this.getCollection(dbName, table).insertMany(MongoDbHelper.mapToDocument(map));
	}
	

    /** 分页查询 */
	@Override
    public MongoCursor<Document> findByPage(MongoCollection<Document> coll, Bson filter, int pageNo, int pageSize,Bson sort) {
        if(pageSize>0){
        	return coll.find(filter).sort(sort).skip((pageNo - 1) * pageSize).limit(pageSize).iterator();
        }
     	return coll.find(filter).sort(sort).iterator();
	}

    /** 分页查询 */
	@Override
    public MongoCursor<Document> findByPage(MongoCollection<Document> coll, Bson filter, int pageNo, int pageSize,String orderField, int order) {
        Bson orderBy = new BasicDBObject(orderField, order);
        if(pageSize>0){
            return coll.find(filter).sort(orderBy).skip((pageNo - 1) * pageSize).limit(pageSize).iterator();
        }
     	return coll.find(filter).sort(orderBy).iterator();
     }

	@Override
	public void createIndex(String dbName, String table, Set<String> index) {
		if(index!=null && !index.isEmpty()){
			Document doc = new Document();
			for (String string : index) {
				doc.append(string, 1);//1:升序，-1：降序 
			}
			IndexOptions d = new IndexOptions();
			d.background(true);
			this.getCollection(dbName, table).createIndex(doc);
		}
	}

	@Override
	public void createIndex(String dbName, String table, IndexBean... index) {
		if(index!=null && index.length>0){
			Document doc = new Document();
			for (IndexBean string : index) {
				doc.append(string.getName(), string.getSort());//1:升序，-1：降序 
			}
			IndexOptions d = new IndexOptions();
			d.background(true);
			this.getCollection(dbName, table).createIndex(doc);
		}
	}
	@Override
	public ListIndexesIterable<Document> findIndex(String dbName, String table){
		return this.getCollection(dbName, table).listIndexes();
	}
	

	@Override
	public void dropIndex(String dbName, String table,IndexBean... index){
		StringBuilder sb = new StringBuilder();
		if(index!=null && index.length>0){
			for (IndexBean string : index) {
				sb.append(string.getName()+"_"+string.getSort());//1:升序，-1：降序 
			}
			this.getCollection(dbName, table).dropIndex(sb.toString());;
		}else{
			this.getCollection(dbName, table).dropIndexes();
		}
	}
}
